package com.qsw.fileoperatelib.iedata;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 数据注入与提取工具
 * 向文件末尾注入数据或从数据末尾提取数据
 * 文件格式：原始文件数据+数据开始标记+原始文件MD5+注入数据条数+注入数据+数据结束标记+原始文件长度
 */
public class IEDataInEndUtil extends BIEDataUtil {

    private static final byte[] INJECT_START = new byte[]{(byte) 0xE7, 0x74, 0x11, 0x19};// 数据开始标记
    private static final byte[] INJECT_END = new byte[]{0x19, 0x11, 0x74, (byte) 0xE7};// 数据结束标记

    private static IEDataInEndUtil mInstance;

    private IEDataInEndUtil() {
    }

    public static IEDataInEndUtil getInstance() {
        if (mInstance == null) {
            synchronized (IEDataInEndUtil.class) {
                if (mInstance == null) {
                    mInstance = new IEDataInEndUtil();
                }
            }
        }
        return mInstance;
    }

    /**
     * 检查是否为已注入文件
     *
     * @param randomAccessFile 待检查的文件
     * @return true：已注入；false：未注入
     * @throws IOException 异常
     */
    @Override
    protected boolean isInjectFile(RandomAccessFile randomAccessFile) throws IOException {
        if (randomAccessFile.length() < (ORIGINAL_FILE_LENGTH_NUMBER_LENGTH + INJECT_START.length + INJECT_END.length + MD5_BYTES_LENGTH)) {
            return false;
        }
        // 读取注入数据结束标志
        randomAccessFile.seek(randomAccessFile.length() - ORIGINAL_FILE_LENGTH_NUMBER_LENGTH - INJECT_END.length);
        byte[] endSignBytes = new byte[INJECT_END.length];
        int readEndSignLength = randomAccessFile.read(endSignBytes);
        if (readEndSignLength <= 0 || !Arrays.equals(endSignBytes, INJECT_END)) {
            return false;
        }
        // 读取原始文件长度
        if (randomAccessFile.getFilePointer() > randomAccessFile.length() - ORIGINAL_FILE_LENGTH_NUMBER_LENGTH) {
            return false;
        }
        long originalFileLength = randomAccessFile.readLong();
        if (originalFileLength > randomAccessFile.length()) {
            return false;
        }
        // 跳转到原始文件结束位置
        randomAccessFile.seek(originalFileLength);

        byte[] startSignBytes = new byte[INJECT_START.length];
        int readStartSignLength = randomAccessFile.read(startSignBytes);

        return readStartSignLength > 0 && Arrays.equals(startSignBytes, INJECT_START);
    }

    /**
     * 向文件中注入信息
     *
     * @param randomAccessFile             待注入信息的文件
     * @param fileMD5Bytes                 待注入文件的原始MD5
     * @param dataList                     待注入的数据
     * @param onSingleTypeProgressListener 进度回调
     * @return 是否成功
     * @throws IOException 异常
     */
    @Override
    protected boolean writeData(RandomAccessFile randomAccessFile, byte[] fileMD5Bytes,
                                List<String> dataList, OnSingleTypeProgressListener onSingleTypeProgressListener) throws IOException {
        long originalFileLength = randomAccessFile.length();

        byte dataCount = (byte) dataList.size();
        long maxProgress = dataCount + 5;
        long progress = 0;
        // 跳到文件末尾
        randomAccessFile.seek(originalFileLength);
        // ###注入开始标记
        randomAccessFile.write(INJECT_START);
        if (onSingleTypeProgressListener != null) {
            progress++;
            onSingleTypeProgressListener.onSingleTypeProgress(progress, maxProgress);
        }

        if (fileMD5Bytes == null || fileMD5Bytes.length != MD5_BYTES_LENGTH) {
            return false;
        }

        // ###注入16个字节的原始文件MD5
        randomAccessFile.write(fileMD5Bytes);
        if (onSingleTypeProgressListener != null) {
            progress++;
            onSingleTypeProgressListener.onSingleTypeProgress(progress, maxProgress);
        }

        // ###注入数据条数
        randomAccessFile.write(new byte[]{dataCount});
        if (onSingleTypeProgressListener != null) {
            progress++;
            onSingleTypeProgressListener.onSingleTypeProgress(progress, maxProgress);
        }

        // ###注入数据
        byte strLength;
        byte[] strBytes;
        for (String str : dataList) {
            strBytes = str.getBytes(DEFAULT_STRING_CHARSET);
            if (strBytes.length > MAX_LENGTH_ONE_MESSAGE) {
                return false;
            }
            strLength = (byte) strBytes.length;

            randomAccessFile.writeByte(strLength);
            randomAccessFile.write(strBytes);

            if (onSingleTypeProgressListener != null) {
                progress++;
                onSingleTypeProgressListener.onSingleTypeProgress(progress, maxProgress);
            }
        }

        // ###注入结束标记
        randomAccessFile.write(INJECT_END);
        if (onSingleTypeProgressListener != null) {
            progress++;
            onSingleTypeProgressListener.onSingleTypeProgress(progress, maxProgress);
        }

        // ###注入原始文件长度
        randomAccessFile.writeLong(originalFileLength);
        if (onSingleTypeProgressListener != null) {
            progress++;
            onSingleTypeProgressListener.onSingleTypeProgress(maxProgress, maxProgress);
        }
        return true;
    }

    /**
     * 读取数据
     *
     * @param randomAccessFile             文件
     * @param onSingleTypeProgressListener 进度回调
     * @return 读取到的数据，如果出错则返回null
     * @throws IOException 异常
     */
    @Override
    protected ReadDataResult readData(RandomAccessFile randomAccessFile, OnSingleTypeProgressListener onSingleTypeProgressListener) throws IOException {
        List<String> resultList = new ArrayList<>();

        /*if (!isInjectFile(randomAccessFile)) {
            return null;
        }*/

        long currFileLength = randomAccessFile.length();
        if (currFileLength < ORIGINAL_FILE_LENGTH_NUMBER_LENGTH) {
            return null;
        }
        randomAccessFile.seek(currFileLength - ORIGINAL_FILE_LENGTH_NUMBER_LENGTH);
        // 读取原始文件长度
        if (randomAccessFile.getFilePointer() > randomAccessFile.length() - ORIGINAL_FILE_LENGTH_NUMBER_LENGTH) {
            return null;
        }
        long originalFileLength = randomAccessFile.readLong();
        if (originalFileLength > randomAccessFile.length()) {
            return null;
        }
        // 跳转到原始文件结束位置
        randomAccessFile.seek(originalFileLength);

        // 读取数据开始标记
        byte[] readBytes = new byte[INJECT_START.length];
        int readStartSignLength = randomAccessFile.read(readBytes);
        if (readStartSignLength != readBytes.length || !Arrays.equals(readBytes, INJECT_START)) {
            return null;
        }

        // 读取原始文件MD5
        byte[] md5Bytes = new byte[MD5_BYTES_LENGTH];
        readStartSignLength = randomAccessFile.read(md5Bytes);
        if (readStartSignLength != md5Bytes.length) {
            return null;
        }

        // 读取数据条数
        if (randomAccessFile.getFilePointer() > randomAccessFile.length() - 1) {
            return null;
        }
        byte dataCount = randomAccessFile.readByte();
        long maxProgress = dataCount + 1;
        long progress = 0;

        // 读取数据
        byte dataLength;
        for (byte i = 0; i < dataCount; i++) {
            if (randomAccessFile.getFilePointer() > randomAccessFile.length() - 1) {
                return null;
            }
            dataLength = randomAccessFile.readByte();

            readBytes = new byte[dataLength];
            readStartSignLength = randomAccessFile.read(readBytes);
            if (readStartSignLength != readBytes.length) {
                return null;
            }

            resultList.add(new String(readBytes, DEFAULT_STRING_CHARSET));

            if (onSingleTypeProgressListener != null) {
                progress++;
                onSingleTypeProgressListener.onSingleTypeProgress(progress, maxProgress);
            }
        }

        // 读取数据结束标记
        readBytes = new byte[INJECT_END.length];
        readStartSignLength = randomAccessFile.read(readBytes);
        if (readStartSignLength != readBytes.length || !Arrays.equals(readBytes, INJECT_END)) {
            return null;
        }

        if (onSingleTypeProgressListener != null) {
            progress++;
            onSingleTypeProgressListener.onSingleTypeProgress(maxProgress, maxProgress);
        }

        return new ReadDataResult(resultList, md5Bytes);
    }

    /**
     * 恢复注入文件为原始文件
     *
     * @param randomAccessFile             待恢复文件
     * @param onSingleTypeProgressListener 进度回调
     * @return true：成功；false：失败
     * @throws IOException 异常
     */
    @Override
    protected boolean recoverFile(RandomAccessFile randomAccessFile, OnSingleTypeProgressListener onSingleTypeProgressListener) throws IOException {
        boolean result = false;
        try {
            if (!isInjectFile(randomAccessFile)) {
                return false;
            }

            long currFileLength = randomAccessFile.length();
            if (currFileLength < ORIGINAL_FILE_LENGTH_NUMBER_LENGTH) {
                return false;
            }
            // 读取原始文件长度
            randomAccessFile.seek(currFileLength - ORIGINAL_FILE_LENGTH_NUMBER_LENGTH);
            if (randomAccessFile.getFilePointer() > randomAccessFile.length() - ORIGINAL_FILE_LENGTH_NUMBER_LENGTH) {
                return false;
            }
            long originalFileLength = randomAccessFile.readLong();
            // 恢复文件为原长度
            randomAccessFile.setLength(originalFileLength);
            result = true;
            if (onSingleTypeProgressListener != null) {
                onSingleTypeProgressListener.onSingleTypeProgress(100, 100);
            }
        } finally {
            if (randomAccessFile != null) {
                randomAccessFile.close();
            }
        }
        return result;
    }
}
